// $Id: int_party.c,v 1.1.1.1 2004/09/10 17:26:51 MagicalTux Exp $
#include "inter.h"
#include "int_party.h"
#include "mmo.h"
#include "char.h"
#include "socket.h"
#include "db.h"
#include "lock.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char party_txt[1024] = "save/party.txt";

static struct dbt *party_db;
static int party_newid = 100;

int  mapif_party_broken (int party_id, int flag);
int  party_check_empty (struct party *p);
int  mapif_parse_PartyLeave (int fd, int party_id, int account_id);

// パーティデータの文字列への変換
int inter_party_tostr (char *str, struct party *p)
{
    int  i, len;

    len =
        sprintf (str, "%d\t%s\t%d,%d\t", p->party_id, p->name, p->exp,
                 p->item);
    for (i = 0; i < MAX_PARTY; i++)
    {
        struct party_member *m = &p->member[i];
        len +=
            sprintf (str + len, "%d,%d\t%s\t", m->account_id, m->leader,
                     ((m->account_id > 0) ? m->name : "NoMember"));
    }

    return 0;
}

// パーティデータの文字列からの変換
int inter_party_fromstr (char *str, struct party *p)
{
    int  i, j;
    int  tmp_int[16];
    char tmp_str[256];

    memset (p, 0, sizeof (struct party));

//  printf("sscanf party main info\n");
    if (sscanf
        (str, "%d\t%[^\t]\t%d,%d\t", &tmp_int[0], tmp_str, &tmp_int[1],
         &tmp_int[2]) != 4)
        return 1;

    p->party_id = tmp_int[0];
    strcpy (p->name, tmp_str);
    p->exp = tmp_int[1];
    p->item = tmp_int[2];
//  printf("%d [%s] %d %d\n", tmp_int[0], tmp_str[0], tmp_int[1], tmp_int[2]);

    for (j = 0; j < 3 && str != NULL; j++)
        str = strchr (str + 1, '\t');

    for (i = 0; i < MAX_PARTY; i++)
    {
        struct party_member *m = &p->member[i];
        if (str == NULL)
            return 1;
//      printf("sscanf party member info %d\n", i);

        if (sscanf
            (str + 1, "%d,%d\t%[^\t]\t", &tmp_int[0], &tmp_int[1],
             tmp_str) != 3)
            return 1;

        m->account_id = tmp_int[0];
        m->leader = tmp_int[1];
        strncpy (m->name, tmp_str, sizeof (m->name));
//      printf(" %d %d [%s]\n", tmp_int[0], tmp_int[1], tmp_str);

        for (j = 0; j < 2 && str != NULL; j++)
            str = strchr (str + 1, '\t');
    }

    return 0;
}

// パーティデータのロード
int inter_party_init ()
{
    char line[8192];
    struct party *p;
    FILE *fp;
    int  c = 0;
    int  i, j;

    party_db = numdb_init ();

    if ((fp = fopen_ (party_txt, "r")) == NULL)
        return 1;

    while (fgets (line, sizeof (line) - 1, fp))
    {
        j = 0;
        if (sscanf (line, "%d\t%%newid%%\n%n", &i, &j) == 1 && j > 0
            && party_newid <= i)
        {
            party_newid = i;
            continue;
        }

        p = calloc (sizeof (struct party), 1);
        if (p == NULL)
        {
            printf ("int_party: out of memory!\n");
            exit (0);
        }
        memset (p, 0, sizeof (struct party));
        if (inter_party_fromstr (line, p) == 0 && p->party_id > 0)
        {
            if (p->party_id >= party_newid)
                party_newid = p->party_id + 1;
            numdb_insert (party_db, p->party_id, p);
            party_check_empty (p);
        }
        else
        {
            printf ("int_party: broken data [%s] line %d\n", party_txt,
                    c + 1);
            free (p);
        }
        c++;
    }
    fclose_ (fp);
//  printf("int_party: %s read done (%d parties)\n", party_txt, c);

    return 0;
}

// パーティーデータのセーブ用
int inter_party_save_sub (void *key, void *data, va_list ap)
{
    char line[8192];
    FILE *fp;

    inter_party_tostr (line, (struct party *) data);
    fp = va_arg (ap, FILE *);
    fprintf (fp, "%s" RETCODE, line);

    return 0;
}

// パーティーデータのセーブ
int inter_party_save ()
{
    FILE *fp;
    int  lock;

    if ((fp = lock_fopen (party_txt, &lock)) == NULL)
    {
        printf ("int_party: cant write [%s] !!! data is lost !!!\n",
                party_txt);
        return 1;
    }
    numdb_foreach (party_db, inter_party_save_sub, fp);
//  fprintf(fp, "%d\t%%newid%%\n", party_newid);
    lock_fclose (fp, party_txt, &lock);
//  printf("int_party: %s saved.\n", party_txt);

    return 0;
}

// パーティ名検索用
int search_partyname_sub (void *key, void *data, va_list ap)
{
    struct party *p = (struct party *) data, **dst;
    char *str;

    str = va_arg (ap, char *);
    dst = va_arg (ap, struct party **);
    if (strcmpi (p->name, str) == 0)
        *dst = p;

    return 0;
}

// パーティ名検索
struct party *search_partyname (char *str)
{
    struct party *p = NULL;
    numdb_foreach (party_db, search_partyname_sub, str, &p);

    return p;
}

// EXP公平分配できるかチェック
int party_check_exp_share (struct party *p)
{
    int  i;
    int  maxlv = 0, minlv = 0x7fffffff;

    for (i = 0; i < MAX_PARTY; i++)
    {
        int  lv = p->member[i].lv;
        if (p->member[i].online)
        {
            if (lv < minlv)
                minlv = lv;
            if (maxlv < lv)
                maxlv = lv;
        }
    }

    return (maxlv == 0 || maxlv - minlv <= party_share_level);
}

// パーティが空かどうかチェック
int party_check_empty (struct party *p)
{
    int  i;

//  printf("party check empty %08X\n", (int)p);
    for (i = 0; i < MAX_PARTY; i++)
    {
//      printf("%d acc=%d\n", i, p->member[i].account_id);
        if (p->member[i].account_id > 0)
        {
            return 0;
        }
    }
    // 誰もいないので解散
    mapif_party_broken (p->party_id, 0);
    numdb_erase (party_db, p->party_id);
    free (p);

    return 1;
}

// キャラの競合がないかチェック用
int party_check_conflict_sub (void *key, void *data, va_list ap)
{
    struct party *p = (struct party *) data;
    int  party_id, account_id, i;
    char *nick;

    party_id = va_arg (ap, int);
    account_id = va_arg (ap, int);
    nick = va_arg (ap, char *);

    if (p->party_id == party_id)    // 本来の所属なので問題なし
        return 0;

    for (i = 0; i < MAX_PARTY; i++)
    {
        if (p->member[i].account_id == account_id
            && strcmp (p->member[i].name, nick) == 0)
        {
            // 別のパーティに偽の所属データがあるので脱退
            printf ("int_party: party conflict! %d %d %d\n", account_id,
                    party_id, p->party_id);
            mapif_parse_PartyLeave (-1, p->party_id, account_id);
        }
    }

    return 0;
}

// キャラの競合がないかチェック
int party_check_conflict (int party_id, int account_id, char *nick)
{
    numdb_foreach (party_db, party_check_conflict_sub, party_id, account_id,
                   nick);

    return 0;
}

//-------------------------------------------------------------------
// map serverへの通信

// パーティ作成可否
int mapif_party_created (int fd, int account_id, struct party *p)
{
    WFIFOW (fd, 0) = 0x3820;
    WFIFOL (fd, 2) = account_id;
    if (p != NULL)
    {
        WFIFOB (fd, 6) = 0;
        WFIFOL (fd, 7) = p->party_id;
        memcpy (WFIFOP (fd, 11), p->name, 24);
        printf ("int_party: created! %d %s\n", p->party_id, p->name);
    }
    else
    {
        WFIFOB (fd, 6) = 1;
        WFIFOL (fd, 7) = 0;
        memcpy (WFIFOP (fd, 11), "error", 24);
    }
    WFIFOSET (fd, 35);

    return 0;
}

// パーティ情報見つからず
int mapif_party_noinfo (int fd, int party_id)
{
    WFIFOW (fd, 0) = 0x3821;
    WFIFOW (fd, 2) = 8;
    WFIFOL (fd, 4) = party_id;
    WFIFOSET (fd, 8);
    printf ("int_party: info not found %d\n", party_id);

    return 0;
}

// パーティ情報まとめ送り
int mapif_party_info (int fd, struct party *p)
{
    unsigned char buf[4 + sizeof (struct party)];

    WBUFW (buf, 0) = 0x3821;
    memcpy (buf + 4, p, sizeof (struct party));
    WBUFW (buf, 2) = 4 + sizeof (struct party);
    if (fd < 0)
        mapif_sendall (buf, WBUFW (buf, 2));
    else
        mapif_send (fd, buf, WBUFW (buf, 2));
//  printf("int_party: info %d %s\n", p->party_id, p->name);

    return 0;
}

// パーティメンバ追加可否
int mapif_party_memberadded (int fd, int party_id, int account_id, int flag)
{
    WFIFOW (fd, 0) = 0x3822;
    WFIFOL (fd, 2) = party_id;
    WFIFOL (fd, 6) = account_id;
    WFIFOB (fd, 10) = flag;
    WFIFOSET (fd, 11);

    return 0;
}

// パーティ設定変更通知
int mapif_party_optionchanged (int fd, struct party *p, int account_id,
                               int flag)
{
    unsigned char buf[15];

    WBUFW (buf, 0) = 0x3823;
    WBUFL (buf, 2) = p->party_id;
    WBUFL (buf, 6) = account_id;
    WBUFW (buf, 10) = p->exp;
    WBUFW (buf, 12) = p->item;
    WBUFB (buf, 14) = flag;
    if (flag == 0)
        mapif_sendall (buf, 15);
    else
        mapif_send (fd, buf, 15);
    printf ("int_party: option changed %d %d %d %d %d\n", p->party_id,
            account_id, p->exp, p->item, flag);

    return 0;
}

// パーティ脱退通知
int mapif_party_leaved (int party_id, int account_id, char *name)
{
    unsigned char buf[34];

    WBUFW (buf, 0) = 0x3824;
    WBUFL (buf, 2) = party_id;
    WBUFL (buf, 6) = account_id;
    memcpy (WBUFP (buf, 10), name, 24);
    mapif_sendall (buf, 34);
    printf ("int_party: party leaved %d %d %s\n", party_id, account_id, name);

    return 0;
}

// パーティマップ更新通知
int mapif_party_membermoved (struct party *p, int idx)
{
    unsigned char buf[29];

    WBUFW (buf, 0) = 0x3825;
    WBUFL (buf, 2) = p->party_id;
    WBUFL (buf, 6) = p->member[idx].account_id;
    memcpy (WBUFP (buf, 10), p->member[idx].map, 16);
    WBUFB (buf, 26) = p->member[idx].online;
    WBUFW (buf, 27) = p->member[idx].lv;
    mapif_sendall (buf, 29);

    return 0;
}

// パーティ解散通知
int mapif_party_broken (int party_id, int flag)
{
    unsigned char buf[7];
    WBUFW (buf, 0) = 0x3826;
    WBUFL (buf, 2) = party_id;
    WBUFB (buf, 6) = flag;
    mapif_sendall (buf, 7);
    printf ("int_party: broken %d\n", party_id);

    return 0;
}

// パーティ内発言
int mapif_party_message (int party_id, int account_id, char *mes, int len)
{
    unsigned char buf[len + 12];

    WBUFW (buf, 0) = 0x3827;
    WBUFW (buf, 2) = len + 12;
    WBUFL (buf, 4) = party_id;
    WBUFL (buf, 8) = account_id;
    memcpy (WBUFP (buf, 12), mes, len);
    mapif_sendall (buf, len + 12);

    return 0;
}

//-------------------------------------------------------------------
// map serverからの通信

// パーティ
int mapif_parse_CreateParty (int fd, int account_id, char *name, char *nick,
                             char *map, int lv)
{
    struct party *p;
    int  i;

    for (i = 0; i < 24 && name[i]; i++)
    {
        if (!(name[i] & 0xe0) || name[i] == 0x7f)
        {
            printf ("int_party: illegal party name [%s]\n", name);
            mapif_party_created (fd, account_id, NULL);
            return 0;
        }
    }

    if ((p = search_partyname (name)) != NULL)
    {
        printf ("int_party: same name party exists [%s]\n", name);
        mapif_party_created (fd, account_id, NULL);
        return 0;
    }
    p = calloc (sizeof (struct party), 1);
    if (p == NULL)
    {
        printf ("int_party: out of memory !\n");
        mapif_party_created (fd, account_id, NULL);
        return 0;
    }
    memset (p, 0, sizeof (struct party));
    p->party_id = party_newid++;
    memcpy (p->name, name, 24);
    p->exp = 0;
    p->item = 0;
    p->member[0].account_id = account_id;
    memcpy (p->member[0].name, nick, 24);
    memcpy (p->member[0].map, map, 16);
    p->member[0].leader = 1;
    p->member[0].online = 1;
    p->member[0].lv = lv;

    numdb_insert (party_db, p->party_id, p);

    mapif_party_created (fd, account_id, p);
    mapif_party_info (fd, p);

    return 0;
}

// パーティ情報要求
int mapif_parse_PartyInfo (int fd, int party_id)
{
    struct party *p;

    p = numdb_search (party_db, party_id);
    if (p != NULL)
        mapif_party_info (fd, p);
    else
        mapif_party_noinfo (fd, party_id);

    return 0;
}

// パーティ追加要求
int mapif_parse_PartyAddMember (int fd, int party_id, int account_id,
                                char *nick, char *map, int lv)
{
    struct party *p;
    int  i;

    p = numdb_search (party_db, party_id);
    if (p == NULL)
    {
        mapif_party_memberadded (fd, party_id, account_id, 1);
        return 0;
    }

    for (i = 0; i < MAX_PARTY; i++)
    {
        if (p->member[i].account_id == 0)
        {
            int  flag = 0;

            p->member[i].account_id = account_id;
            memcpy (p->member[i].name, nick, 24);
            memcpy (p->member[i].map, map, 16);
            p->member[i].leader = 0;
            p->member[i].online = 1;
            p->member[i].lv = lv;
            mapif_party_memberadded (fd, party_id, account_id, 0);
            mapif_party_info (-1, p);

            if (p->exp > 0 && !party_check_exp_share (p))
            {
                p->exp = 0;
                flag = 0x01;
            }
            if (flag)
                mapif_party_optionchanged (fd, p, 0, 0);
            return 0;
        }
    }
    mapif_party_memberadded (fd, party_id, account_id, 1);

    return 0;
}

// パーティー設定変更要求
int mapif_parse_PartyChangeOption (int fd, int party_id, int account_id,
                                   int exp, int item)
{
    struct party *p;
    int  flag = 0;

    p = numdb_search (party_db, party_id);
    if (p == NULL)
        return 0;

    p->exp = exp;
    if (exp > 0 && !party_check_exp_share (p))
    {
        flag |= 0x01;
        p->exp = 0;
    }

    p->item = item;

    mapif_party_optionchanged (fd, p, account_id, flag);
    return 0;
}

// パーティ脱退要求
int mapif_parse_PartyLeave (int fd, int party_id, int account_id)
{
    struct party *p;
    int  i;

    p = numdb_search (party_db, party_id);
    if (p != NULL)
    {
        for (i = 0; i < MAX_PARTY; i++)
        {
            if (p->member[i].account_id == account_id)
            {
                mapif_party_leaved (party_id, account_id, p->member[i].name);

                memset (&p->member[i], 0, sizeof (struct party_member));
                if (party_check_empty (p) == 0)
                    mapif_party_info (-1, p);   // まだ人がいるのでデータ送信
                return 0;
            }
        }
    }

    return 0;
}

// パーティマップ更新要求
int mapif_parse_PartyChangeMap (int fd, int party_id, int account_id,
                                char *map, int online, int lv)
{
    struct party *p;
    int  i;

    p = numdb_search (party_db, party_id);
    if (p == NULL)
        return 0;

    for (i = 0; i < MAX_PARTY; i++)
    {
        if (p->member[i].account_id == account_id)
        {
            int  flag = 0;

            memcpy (p->member[i].map, map, 16);
            p->member[i].online = online;
            p->member[i].lv = lv;
            mapif_party_membermoved (p, i);

            if (p->exp > 0 && !party_check_exp_share (p))
            {
                p->exp = 0;
                flag = 1;
            }
            if (flag)
                mapif_party_optionchanged (fd, p, 0, 0);
            break;
        }
    }

    return 0;
}

// パーティ解散要求
int mapif_parse_BreakParty (int fd, int party_id)
{
    struct party *p;

    p = numdb_search (party_db, party_id);
    if (p == NULL)
        return 0;

    numdb_erase (party_db, party_id);
    mapif_party_broken (fd, party_id);

    return 0;
}

// パーティメッセージ送信
int mapif_parse_PartyMessage (int fd, int party_id, int account_id, char *mes,
                              int len)
{
    return mapif_party_message (party_id, account_id, mes, len);
}

// パーティチェック要求
int mapif_parse_PartyCheck (int fd, int party_id, int account_id, char *nick)
{
    return party_check_conflict (party_id, account_id, nick);
}

// map server からの通信
// ・１パケットのみ解析すること
// ・パケット長データはinter.cにセットしておくこと
// ・パケット長チェックや、RFIFOSKIPは呼び出し元で行われるので行ってはならない
// ・エラーなら0(false)、そうでないなら1(true)をかえさなければならない
int inter_party_parse_frommap (int fd)
{
    switch (RFIFOW (fd, 0))
    {
        case 0x3020:
            mapif_parse_CreateParty (fd, RFIFOL (fd, 2), RFIFOP (fd, 6),
                                     RFIFOP (fd, 30), RFIFOP (fd, 54),
                                     RFIFOW (fd, 70));
            break;
        case 0x3021:
            mapif_parse_PartyInfo (fd, RFIFOL (fd, 2));
            break;
        case 0x3022:
            mapif_parse_PartyAddMember (fd, RFIFOL (fd, 2), RFIFOL (fd, 6),
                                        RFIFOP (fd, 10), RFIFOP (fd, 34),
                                        RFIFOW (fd, 50));
            break;
        case 0x3023:
            mapif_parse_PartyChangeOption (fd, RFIFOL (fd, 2), RFIFOL (fd, 6),
                                           RFIFOW (fd, 10), RFIFOW (fd, 12));
            break;
        case 0x3024:
            mapif_parse_PartyLeave (fd, RFIFOL (fd, 2), RFIFOL (fd, 6));
            break;
        case 0x3025:
            mapif_parse_PartyChangeMap (fd, RFIFOL (fd, 2), RFIFOL (fd, 6),
                                        RFIFOP (fd, 10), RFIFOB (fd, 26),
                                        RFIFOW (fd, 27));
            break;
        case 0x3026:
            mapif_parse_BreakParty (fd, RFIFOL (fd, 2));
            break;
        case 0x3027:
            mapif_parse_PartyMessage (fd, RFIFOL (fd, 4), RFIFOL (fd, 8),
                                      RFIFOP (fd, 12), RFIFOW (fd, 2) - 12);
            break;
        case 0x3028:
            mapif_parse_PartyCheck (fd, RFIFOL (fd, 2), RFIFOL (fd, 6),
                                    RFIFOP (fd, 10));
            break;
        default:
            return 0;
    }

    return 1;
}

// サーバーから脱退要求（キャラ削除用）
int inter_party_leave (int party_id, int account_id)
{
    return mapif_parse_PartyLeave (-1, party_id, account_id);
}
